﻿using System;
using System.Collections.Generic;
using System.Configuration.Provider;
using System.Linq;
using System.Web;
using System.Web.Security;
using MovingChame.Web.Helpers;
using MovingChame.Web.Models;

namespace MovingChame.Web.Infrastructure
{
    public class MovingChameRoleProvider : RoleProvider
    {
        private string connectionStringName;

        private string providerName;

        MovingChameModelContainer db = null;

        private string applicationName;
        public override string ApplicationName
        {
            get { return applicationName; }
            set { applicationName = value; }
        }

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);

            providerName = name;

            if (config["applicationName"] != null)
                applicationName = config["applicationName"];

            if (config["connectionStringName"] != null)
            {
                connectionStringName = config["connectionStringName"];
                db = new MovingChameModelContainer();
                db.Database.Connection.ConnectionString = ConfigurationHelper.GetConnectionString(connectionStringName);
                db.Database.Connection.Open();
            }

            // Add default
            if (!RoleExists(Constants.RoleAdmin))
                CreateRole(Constants.RoleAdmin);

            if (!RoleExists(Constants.RoleUser))
                CreateRole(Constants.RoleUser);

            if (!RoleExists(Constants.RoleDeveloper))
                CreateRole(Constants.RoleDeveloper);
        }

        public override void AddUsersToRoles(string[] userNames, string[] roleNames)
        {
            // Validate
            foreach (var roleName in roleNames)
            {
                if (string.IsNullOrEmpty(roleName))
                    throw new ProviderException("Role name cannot be null or empty!");
                if (!RoleExists(roleName))
                    throw new ProviderException("Role '" + roleName + "' does not exist!");
            }

            foreach (var userName in userNames)
            {
                if (string.IsNullOrEmpty(userName))
                    throw new ProviderException("User name cannot be null or empty!");
                if (db.Users.SingleOrDefault(o => o.Username == userName) == null)
                    throw new ProviderException("User '" + userName + "' does not exist!");
                foreach (var roleName in roleNames)
                    if (IsUserInRole(userName, roleName))
                        throw new ProviderException("User '" + userName + "' is already in Role '" + roleName + "'!");
            }

            foreach (var username in userNames)
                foreach (var rolename in roleNames)
                {
                    var user = db.Users.SingleOrDefault(o => o.Username == username);
                    var role = db.Roles.SingleOrDefault(o => o.Name == rolename);

                    if (user != null && role != null && !user.Roles.Contains(role))
                    {
                        user.Roles.Add(role);
                        role.Users.Add(user);
                    }
                }
            db.SaveChanges();
        }

        public override void CreateRole(string roleName)
        {
            if (RoleExists(roleName))
                throw new ProviderException("Role '" + roleName + "' already exists!");

            db.Roles.Add(new Role { Name = roleName });
            db.SaveChanges();
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            var role = db.Roles.SingleOrDefault(o => o.Name == roleName);

            if (role == null)
                return false;

            if (throwOnPopulatedRole && role.Users.Count() != 0)
                throw new InvalidOperationException("Role " + roleName + " is still in use!");

            db.Roles.Remove(role);
            db.SaveChanges();

            return true;
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            var role = db.Roles.SingleOrDefault(o => o.Name == roleName);

            if (role == null)
                return null;

            return role.Users.Where(o => o.Username.Contains(usernameToMatch)).Select(o => o.Username).ToArray();
        }

        public override string[] GetAllRoles()
        {
            return db.Roles.Select(o => o.Name).ToArray();
        }

        public override string[] GetRolesForUser(string username)
        {
            var user = db.Users.SingleOrDefault(o => o.Username == username);

            if (user == null)
                return null;

            return user.Roles.Select(o => o.Name).ToArray();
        }

        public override string[] GetUsersInRole(string roleName)
        {
            var role = db.Roles.SingleOrDefault(o => o.Name == roleName);

            if (role == null)
                return null;

            return role.Users.Select(o => o.Username).ToArray();
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            return db.Users.SingleOrDefault(o => o.Username == username && o.Roles.Select(r => r.Name).Contains(roleName)) != null;
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            foreach (var username in usernames)
                foreach (var rolename in roleNames)
                {
                    var user = db.Users.SingleOrDefault(o => o.Username == username);
                    var role = db.Roles.SingleOrDefault(o => o.Name == rolename);

                    if (user != null && role != null && user.Roles.Contains(role))
                    {
                        user.Roles.Remove(role);
                        role.Users.Remove(user);
                    }
                }
            db.SaveChanges();
        }

        public override bool RoleExists(string roleName)
        {
            return db.Roles.SingleOrDefault(o => o.Name == roleName) != null;
        }
    }
}